#pragma once
#ifndef CONSTRUCTONFIRSTUSE
#define CONSTRUCTONFIRSTUSE

#include <cstddef>
#include <new>
#include <type_traits>

#include "LowLevel.h"

//Inspiriert von hier:
//http://www.codeproject.com/Articles/18948/C-Static-Initialization-Order-A-New-Solution-to-an

template<class T>
struct ConstructOnFirstUseDefaultContructor
{
    static FORCEINLINE T* Construct(void* DataVoidPointer)
    {
        return new(DataVoidPointer) T;
    }
};

template<class T, class OnConstruction = ConstructOnFirstUseDefaultContructor<T>>
struct ConstructOnFirstUse
{
private:
    char DataInternal[sizeof T];
    T* DataPointer;

public:
    FORCEINLINE ConstructOnFirstUse()
    {
        DataPointer = NULL;
    }
    FORCEINLINE ~ConstructOnFirstUse()
    {
        this->Destruct();
    }

    FORCEINLINE void Construct()
    {
        void* const DataVoidPointer = reinterpret_cast<void*>(&(DataInternal[0]));
        this->DataPointer = OnConstruction::Construct(DataVoidPointer);
    }
    FORCEINLINE    void ConstructIfRequired()
    {
        if(!(this->DataPointer))
            this->Construct();
    }
    FORCEINLINE bool IsConstructed()
    {
        return this->Construct != NULL;
    }
    FORCEINLINE bool Destruct()
    {
        if (this->DataPointer)
        {
            this->DataPointer->T::~T();
            return true;
        }
        return false;
    }

    FORCEINLINE T& operator =(T& Other)
    {
        static_assert(!(std::is_const<T>::value), "It is not allowed to assing a const value.");
        this->ConstructIfRequired();
        (*(this->DataPointer)) = Other;
        return *(this->DataPointer);
    }
    FORCEINLINE operator T&()
    {
        this->ConstructIfRequired();
        return *(this->DataPointer);
    }
    FORCEINLINE T* operator &()
    {
        this->ConstructIfRequired();
        return this->DataPointer;
    }
    FORCEINLINE T* operator ->()
    {
        this->ConstructIfRequired();
        return this->DataPointer;
    }
};

#endif